﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using System.Numerics;
using SpiceNet.Parameters;
using SpiceNet.Simulations;

namespace SpiceNet.Models
{
    /// <summary>
    /// A voltage controlled voltage source
    /// </summary>
    public class VoltageControlledVoltageSource : Instance
    {
        /*
         * NOTES:
         * - The VCVSstates parameter is never allocated or stored, so I removed it from this source file.
         */

        /// <summary>
        /// Get parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        #region Parameters
        private enum _c
        {
            VCVS_GAIN = 1,
            VCVS_POS_NODE = 2,
            VCVS_NEG_NODE = 3,
            VCVS_CONT_P_NODE = 4,
            VCVS_CONT_N_NODE = 5,
            VCVS_BR = 6,
            VCVS_IC = 7,
            VCVS_CONT_V_OLD = 8,
            VCVS_GAIN_SENS = 9,
            VCVS_CURRENT = 10,
            VCVS_POWER = 11,
            VCVS_VOLTS = 12,

            /* model parameters */

            /* device questions */
            VCVS_QUEST_SENS_REAL = 201,
            VCVS_QUEST_SENS_IMAG = 202,
            VCVS_QUEST_SENS_MAG = 203,
            VCVS_QUEST_SENS_PH = 204,
            VCVS_QUEST_SENS_CPLX = 205,
            VCVS_QUEST_SENS_DC = 206
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "gain", new IP(IF.IOPU, (int)_c.VCVS_GAIN, T.REAL, "Voltage gain") },
            { "sens_gain", new IP(IF.IP, (int)_c.VCVS_GAIN_SENS, T.FLAG, "flag to request sensitivity WRT gain") },
            { "pos_node", new IP(IF.OPU, (int)_c.VCVS_POS_NODE, T.INTEGER, "Positive node of source") },
            { "neg_node", new IP(IF.OPU, (int)_c.VCVS_NEG_NODE, T.INTEGER, "Negative node of source") },
            { "cont_p_node", new IP(IF.OPU, (int)_c.VCVS_CONT_P_NODE, T.INTEGER, "Positive node of contr. source") },
            { "cont_n_node", new IP(IF.OPU, (int)_c.VCVS_CONT_N_NODE, T.INTEGER, "Negative node of contr. source") },
            { "ic", new IP(IF.IP, (int)_c.VCVS_IC, T.REAL, "Initial condition of controlling source") },
            { "i", new IP(IF.OP, (int)_c.VCVS_CURRENT, T.REAL, "Output current") },
            { "v", new IP(IF.OP, (int)_c.VCVS_VOLTS, T.REAL, "Output voltage") },
            { "p", new IP(IF.OP, (int)_c.VCVS_POWER, T.REAL, "Power") },
            { "sens_dc", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_DC, T.REAL, "dc sensitivity ") },
            { "sens_real", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_REAL, T.REAL, "real part of ac sensitivity") },
            { "sens_imag", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_IMAG, T.REAL, "imag part of ac sensitivity") },
            { "sens_mag", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_MAG, T.REAL, "sensitivity of ac magnitude") },
            { "sens_ph", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_PH, T.REAL, "sensitivity of ac phase") },
            { "sens_cplx", new IP(IF.OPU, (int)_c.VCVS_QUEST_SENS_CPLX, T.COMPLEX, "ac sensitivity") }
        };

        public Parameter<double> VCVScoeff { get; } = new Parameter<double>();
        public double VCVSinitCond { get; set; }
        public int VCVSsenParmNo { get; set; }
        #endregion

        /// <summary>
        /// Private variables
        /// </summary>
        private int VCVSposNode;
        private int VCVSnegNode;
        private int VCVScontPosNode;
        private int VCVScontNegNode;
        public int VCVSbranch { get; private set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public VoltageControlledVoltageSource(string name) : base(name, 4, typeof(VoltageControlledVoltageSourceModel)) { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the Voltage-Controlled Voltage source</param>
        /// <param name="nplus">The name of the positive output node</param>
        /// <param name="nminus">The name of the negative output node</param>
        /// <param name="ncplus">The name of the positive input node</param>
        /// <param name="ncminus">The name of the negative input node</param>
        /// <param name="gain">The voltage gain (V/V)</param>
        public VoltageControlledVoltageSource(string name, string nplus, string nminus, string ncplus, string ncminus, double gain) : this(name)
        {
            Connect(nplus, nminus, ncplus, ncminus);
            VCVScoeff.Par(gain);
        }

        /// <summary>
        /// Set parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.VCVS_GAIN:
                    VCVScoeff.Par((double)value);
                    break;
                case _c.VCVS_GAIN_SENS:
                    VCVSsenParmNo = (int)value;
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.VCVS_POS_NODE:
                    return VCVSposNode;
                case _c.VCVS_NEG_NODE:
                    return VCVSnegNode;
                case _c.VCVS_CONT_P_NODE:
                    return VCVScontPosNode;
                case _c.VCVS_CONT_N_NODE:
                    return VCVScontNegNode;
                case _c.VCVS_IC:
                    return VCVSinitCond;
                case _c.VCVS_GAIN:
                    return VCVScoeff.Value;
                case _c.VCVS_CONT_V_OLD:
                    // return ckt.State.States[0][VCVSstate + VCVScontVOld];
                    return null; // Never allocated
                case _c.VCVS_BR:
                    return VCVSbranch;
                case _c.VCVS_CURRENT:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return ckt.State.Solution[VCVSbranch];
                    }
                case _c.VCVS_VOLTS:
                    return (ckt.State.Solution[VCVSposNode] -
                    ckt.State.Solution[VCVSnegNode]);
                case _c.VCVS_POWER:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return ckt.State.Solution[VCVSbranch] *
                                (ckt.State.Solution[VCVSposNode] -
                                ckt.State.Solution[VCVSnegNode]);
                    }
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Setup the VCVS
        /// </summary>
        /// <param name="ckt"></param>
        /// <param name="model"></param>
        /// <param name="p"></param>
        public override void Setup(Circuit ckt, Model model, object p = null)
        {
            var nodes = BindNodes(ckt, NodeType.Current);
            VCVSposNode = nodes[0].Number;
            VCVSnegNode = nodes[1].Number;
            VCVScontPosNode = nodes[2].Number;
            VCVScontNegNode = nodes[3].Number;
            VCVSbranch = nodes[4].Number;
        }

        /// <summary>
        /// Temperature-dependent calculations
        /// </summary>
        /// <param name="ckt"></param>
        /// <param name="model"></param>
        /// <param name="p"></param>
        public override void Temperature(Circuit ckt, Model model, object p = null)
        {
        }

        /// <summary>
        /// Load the VCVS
        /// </summary>
        /// <param name="ckt"></param>
        /// <param name="model"></param>
        /// <param name="p"></param>
        public override void Load(Circuit ckt, Model model, object p = null)
        {
            CircuitState state = ckt.State;
            state.Matrix[VCVSposNode, VCVSbranch] += 1.0;
            state.Matrix[VCVSbranch, VCVSposNode] += 1.0;
            state.Matrix[VCVSnegNode, VCVSbranch] -= 1.0;
            state.Matrix[VCVSbranch, VCVSnegNode] -= 1.0;
            state.Matrix[VCVSbranch, VCVScontPosNode] -= VCVScoeff;
            state.Matrix[VCVSbranch, VCVScontNegNode] += VCVScoeff;
        }

        /// <summary>
        /// Load the VCVS for AC anslysis
        /// </summary>
        /// <param name="ckt"></param>
        /// <param name="model"></param>
        /// <param name="p"></param>
        public override void AcLoad(Circuit ckt, Model model, object p = null)
        {
            CircuitStateComplex state = ckt.StateComplex;
            state.Matrix[VCVSposNode, VCVSbranch] += 1.0;
            state.Matrix[VCVSbranch, VCVSposNode] += 1.0;
            state.Matrix[VCVSnegNode, VCVSbranch] -= 1.0;
            state.Matrix[VCVSbranch, VCVSnegNode] -= 1.0;
            state.Matrix[VCVSbranch, VCVScontPosNode] -= VCVScoeff.Value;
            state.Matrix[VCVSbranch, VCVScontNegNode] += VCVScoeff.Value;
        }
    }
}
